package com.crl.nfp.filter;

import com.crl.nfp.feign.AuthorizationClient;
import com.crl.nfp.feign.CanAccessDTO;
import com.crl.nfp.log.model.LogModel;
import com.crl.nfp.log.service.LogService;
import com.crl.nfp.util.Params;
import com.crl.nfp.util.RequestUtil;
import com.crl.nfp.util.ZuulConstUtil;
import com.cars.util.date.DateUtil;
import com.cars.util.global.GlobalReturnCode;
import com.cars.util.json.JsonResult;
import com.cars.util.json.JsonUtil;
import com.cars.util.jwt.JwtUtil;
import com.cars.util.string.StringUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.regex.Pattern;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVICE_ID_KEY;

/**
 * 权限过滤器，权限不通过不路由直接返回
 *
 * @author 宋长军
 * @date 2019/3/31 9:45
 */
public class CheckAuthorizationFilter extends ZuulFilter {

    @Autowired
    private AuthorizationClient authorizationClient;
    @Autowired
    private LogService logService;
    @Value("${zuulPermitUrl}")
    private String permitUrl;
    @Value("${zuulPermitServer}")
    private String permitServer;
    @Value(("${zuulPermitServiceUrl}"))
    private String permitServiceUrl;

    /**
     * 存储无需鉴权的请求列表
     */
    private ArrayList<String> permitUrlList = new ArrayList<>();
    private final Logger LOGGER = LoggerFactory.getLogger("operationLog");

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return PRE_DECORATION_FILTER_ORDER + 1;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        return requestContext.sendZuulResponse();
    }

    @Override
    public Object run() {

        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        String servletPath = request.getServletPath();

        // 无需鉴权
        if (isPermit(servletPath)) {
            requestContext.setSendZuulResponse(true);
            LOGGER.info("OPERATIONLOG - TIME：{} - API:{}", DateUtil.getSystemTime(), servletPath);
            return null;
        }

        /*
            TOKEN 请求方式
         */
        String token = request.getHeader("Authorization");
        if (!StringUtils.isEmpty(token)) {

            JsonResult jsonResult =
                    authorizationClient.canAccess(new CanAccessDTO(servletPath, token));
            String returnCode = jsonResult.getReturnCode();
            boolean canAccess = GlobalReturnCode.OK.getReturnCode().equals(returnCode);

            // 请求校验通过
            if (canAccess) {
                requestContext.setResponseStatusCode(HttpStatus.OK.value());
            }
            // 请求未通过
            else {
                setResponse(HttpStatus.UNAUTHORIZED.value(), jsonResult, 1, token);
            }
            requestContext.setSendZuulResponse(canAccess);
            LOGGER.info("OPERATIONLOG - TOKEN:{} - TIME：{} - API:{}", token, DateUtil.getSystemTime(), servletPath);

        } else {
            // 校验未通过
            setResponse(HttpStatus.BAD_REQUEST.value(), new JsonResult(GlobalReturnCode.BAD_REQUEST), 0, "null");
            requestContext.setSendZuulResponse(false);
            LOGGER.info("OPERATIONLOG - NO_PERMISSION_REQUEST - TIME：{} - API:{}", DateUtil.getSystemTime(), servletPath);
        }
        return null;
    }

    /**
     * 设置相应
     *
     * @param httpStatus 返回码
     * @param jsonResult 返回值
     */
    private void setResponse(int httpStatus, JsonResult jsonResult, int authType, String owner) {

        logging(authType, owner, jsonResult.getReturnCode(), jsonResult.getMsg());
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletResponse response = requestContext.getResponse();
        response.setStatus(httpStatus);
        response.setContentType("application/json;charset=UTF-8");
        requestContext.setResponseBody(JsonUtil.toStr(jsonResult));
    }

    /**
     * 判断请求是否是直接路由请求
     *
     * @param url 请求url
     * @return true：直接路由
     */
    private boolean isPermit(String url) {

        if (CollectionUtils.isEmpty(permitUrlList)) {
            Collections.addAll(permitUrlList, permitUrl.split(","));
        }
        if (permitUrlList.stream()
                .anyMatch(url::equals)) {
            return true;
        }
        for (String serverName :
                permitServer.split(",")) {
            Pattern permitPattern = Pattern.compile("^" + serverName + "\\D*" + permitServiceUrl + "$");
            boolean permitServerResult = permitPattern.matcher(url).matches();
            if (permitServerResult) {
                return true;
            }
        }
        return false;
    }

    /**
     * 记录无权限请求的日志
     *
     * @param authType   请求类型
     * @param owner      请求者
     * @param returnCode 响应码
     * @param returnMsg  响应信息
     */
    private void logging(int authType, String owner, String returnCode, String returnMsg) {

        try {
            RequestContext requestContext = RequestContext.getCurrentContext();
            HttpServletRequest httpRequest = requestContext.getRequest();
            LogModel logModel = new LogModel();
            String requestParams = RequestUtil.getRequestParams(httpRequest);
            Params params = new Params(requestParams, returnMsg);
            logModel.setReturnCode(returnCode);
            logModel.setServiceName((String) requestContext.get(SERVICE_ID_KEY));
            logModel.setRequestDate((Date) requestContext.get(ZuulConstUtil.REQUEST_DATE));
            logModel.setPath(httpRequest.getRequestURI());
            logModel.setParamsStr(JsonUtil.toStr(params));
            logModel.setParams(JsonUtil.toStr(params).getBytes("GBK"));
            logModel.setClientIp(StringUtil.getIp(httpRequest));
            if (ZuulConstUtil.PASSWORD_AUTH_TYPE.equals(authType)) {
                logModel.setAuthType(ZuulConstUtil.PASSWORD_AUTH_TYPE);
                logModel.setOwner(JwtUtil.getJWTOwner(owner));
            } else {
                logModel.setAuthType(ZuulConstUtil.APPID_AUTH_TYPE);
                logModel.setOwner(owner);
            }
            logModel.setErrorType(0);
            RequestUtil.setTimeConsuming(logModel);
            logService.insertErrorLog(logModel);
        } catch (Exception e) {
            LOGGER.error("", e);
        }
    }
}
